//! # Machine Status Register
//! mstatus 用于跟踪当前 hart 的操作（控制的）状态，是一个可读写的寄存器。
//!
//! > S-level ISA 中的 sstatus 寄存器是受限制的 mstatus 视图。
//!
//! # 特权以及全局中断使能栈
//! + MIE 用于 M-mode
//! + SIE 用于 S-mode
//! 这两个 bit 用于确保当前特权模式下的中断处理的原子性，这种原子性的要求是单指令禁用中断，
//! 这也是为什么 xIE 位于 mstatus 寄存器的低 bit 位。
//!
//! ## 嵌套 trap
//! + xPIE 保存 trap 之前活动的中断使能位
//! + xPP 保存之前的特权模式
//!
//! 低特权模式，任何 trap 是发生再更高特权模式下，且一进入中断便被禁止。
//! 更高级（特权模式）的 trap handler 要么提供 trap 服务，并使用栈信息返回。
//! 或者，如果不立即返回到被中断的上下文，将在重新使能中断之前保存特权栈，因此每个栈只需要一个入口。
//!
//! 从特权模式 y trap 到特权模式 x 时，xPIE 设置成 xIE；xIE 设置成 0；xPP 设置成 y；
//!
//! xRET 执行时，xIE 会被设置成 xPIE(trap 进入时保存的进入之前的 xIE 设置)，
//! 特权模式也更改成 xPP 中保存的模式。
//! 且 xPIE 设置成 1（也就是之前进入中断之后禁用中断的状态），xPP 设置成最低特权模式。
//! 如果 xPP != M，xRET 还会讲 MPRV 设置成 0。
//!
//! M-mode 软件可以通过在 trap 中读取 MPP 来判断 U-mode 是否被实现。
//!
//! ## 内存特权
//!
//! ### MPRV(Modify PRiVilege)
//!
//! MPRV 用于修改 load 和 store 操作执行时的特权级别。
//! 1. MPRV = 0，load 和 store 使用当前特权模式的翻译和保护机制。
//! 2. MPRV = 1，load 和 store 的地址翻译和保护以及 endianness 使用 MPP 中设置的特权级别。
//!
//! 如果不支持 U-mode，则 MPRV 为只读 0。
//!
//! ### MXR(Make eXecutable Readable)
//!
//! 用于修改访问虚拟内存的特权。
//! 1. MXR = 0，只有从可读页面进行加载操作才会成功。
//! 2. MXR = 1, 从可读或可执行的页面加载都会成功。
//!
//! 如果没有使用基于分页的虚拟内存，则 MXR 无效。
//! 如果不支持 S-mode，MXR 为只读 0.
//!
//! ### SUM (permit Supervisor User Memory access)
//!
//! 用于修改 S-mode 读写虚拟内存时的特权级别。
//!
//! 1. SUM = 0，S-mode 访问允许被 U-mode 访问的页会导致 fault
//! 2. SUM = 1，S-mode 可以访问那些允许被 U-mode 访问的页。
//!
//! 如果没有使用基于分页的虚拟内存，则 SUM 无效。
//! 当 MPRV = 1 且 MPP = S，级别当前特权级别不是 S-mode，SUM 仍会起效。
//!
//!
//! ## Endianness 控制
//!
//! xBE 字段用于控制 M-mode, S-mode 和 U-mode 下内存访问的 Endianness（不包含指令获取）
//!
//! ## 基本 ISA 控制
//!
//! SXL、UXL 用于控制 S-mode 和 U-mode 下 XLEN

use core::{marker::PhantomData, ops::RangeInclusive};

use bits::{BitsOps, IntoBits};

use crate::{csrc, csrci, csrr, csrs, csrsi, csrw, PrivilegeLevel};

const SIE_POS: u32 = 1;
const MIE_POS: u32 = 3;
const SPIE_POS: u32 = 5;
const UBE_POS: u32 = 6;
const MPIE_POS: u32 = 7;
const SPP_POS: u32 = 8;
const VS_POS: RangeInclusive<u32> = 9..=10;
const MPP_POS: RangeInclusive<u32> = 11..=12;
const FS_POS: RangeInclusive<u32> = 13..=14;
const XS_POS: RangeInclusive<u32> = 15..=16;
const MPRV_POS: u32 = 17;
const SUM_POS: u32 = 18;
const MXR_POS: u32 = 19;
const TVM_POS: u32 = 20;
const TW_POS: u32 = 21;
const TSR_POS: u32 = 22;
const SBE_POS: u32 = 36;
const MBE_POS: u32 = 37;

#[cfg(target_arch = "riscv64")]
pub(crate) const UXL_POS: RangeInclusive<u32> = 32..=33;

#[cfg(target_arch = "riscv64")]
pub(crate) const SXL_POS: RangeInclusive<u32> = 34..=35;

#[cfg(target_arch = "riscv64")]
pub(crate) const SD_POS: u32 = 63;

#[cfg(target_arch = "riscv32")]
pub(crate) const SD_POS: u32 = 31;

pub fn read() -> usize {
    csrr!(mstatus)
}
pub fn write(reg_val: usize) {
    csrw!(mstatus, reg_val)
}
pub fn cfg_by_pos(enable: bool, pos: usize) {
    let mask = 1usize << pos;
    if enable {
        csrs!(mstatus, mask);
    } else {
        csrc!(mstatus, mask);
    }
}
pub fn sie(enable: bool) {
    if enable {
        csrsi!(mstatus, 0b1);
    } else {
        csrci!(mstatus, 0b1);
    }
}
pub fn mie(enable: bool) {
    if enable {
        csrsi!(mstatus, 0b1000);
    } else {
        csrci!(mstatus, 0b1000);
    }
}

/// 禁用或启用 mie 和 sie。本函数可以将 mie(), sie() 两个函数合并成一个原子操作。
pub fn xie(enable: bool) {
    if enable {
        csrsi!(mstatus, 0b1001);
    } else {
        csrci!(mstatus, 0b1001);
    }
}
pub fn mprv(enable: bool) {
    cfg_by_pos(enable, MPRV_POS as usize)
}
pub fn mxr(enable: bool) {
    cfg_by_pos(enable, MXR_POS as usize)
}
pub fn sum(enable: bool) {
    cfg_by_pos(enable, SUM_POS as usize)
}

pub fn cache() -> Cache {
    let reg_val = read();
    Cache::from(reg_val)
}

pub struct Cache {
    pub sie: bool,
    pub mie: bool,
    pub spie: bool,
    pub ube: bool,
    pub mpie: bool,
    pub spp: PrivilegeLevel,
    /// vector status
    pub vs: ExtCtxStatus,
    pub mpp: PrivilegeLevel,
    /// float status
    pub fs: ExtCtxStatus,
    /// addition user-mode extensions and associated status
    pub xs: ExtCtxStatus,
    pub mprv: bool,
    pub sum: bool,
    pub mxr: bool,
    /// Trap Virtual Memory
    pub tvm: bool,
    /// Timeout Wait，用于支持截获 WFI 指令
    pub tw: bool,
    /// Trap SRET，用于支持截获 supervisor exception 返回指令
    pub tsr: bool,

    #[cfg(target_arch = "riscv64")]
    pub uxl: u8,
    #[cfg(target_arch = "riscv64")]
    pub sxl: u8,
    #[cfg(target_arch = "riscv64")]
    pub sbe: bool,
    #[cfg(target_arch = "riscv64")]
    pub mbe: bool,

    pub sd: bool,
    phatom: PhantomData<usize>,
}
impl From<usize> for Cache {
    fn from(value: usize) -> Self {
        Self {
            sie: value.bits(SIE_POS).is_set(),
            mie: value.bits(MIE_POS).is_set(),
            spie: value.bits(SPIE_POS).is_set(),
            ube: value.bits(UBE_POS).is_set(),
            mpie: value.bits(MPIE_POS).is_set(),
            spp: (value.bits(SPP_POS).read() as usize).into(),
            vs: ExtCtxStatus::from_raw_unchecked(value.bits(VS_POS).read()),
            mpp: (value.bits(MPP_POS).read() as usize).into(),
            fs: ExtCtxStatus::from_raw_unchecked(value.bits(FS_POS).read()),
            xs: ExtCtxStatus::from_raw_unchecked(value.bits(XS_POS).read()),
            mprv: value.bits(MPRV_POS).is_set(),
            sum: value.bits(SUM_POS).is_set(),
            mxr: value.bits(MXR_POS).is_set(),
            tvm: value.bits(TVM_POS).is_set(),
            tw: value.bits(TW_POS).is_set(),
            tsr: value.bits(TSR_POS).is_set(),
            #[cfg(target_arch = "riscv64")]
            uxl: value.bits(UXL_POS).read() as u8,
            #[cfg(target_arch = "riscv64")]
            sxl: value.bits(SXL_POS).read() as u8,
            #[cfg(target_arch = "riscv64")]
            sbe: value.bits(SBE_POS).is_set(),
            #[cfg(target_arch = "riscv64")]
            mbe: value.bits(MBE_POS).is_set(),
            sd: value.bits(SD_POS).is_set(),
            phatom: PhantomData,
        }
    }
}
impl Into<usize> for Cache {
    fn into(self) -> usize {
        let ret = 0usize
            .bits(SIE_POS)
            .write(self.sie as usize)
            .bits(MIE_POS)
            .write(self.mie as usize)
            .bits(SPIE_POS)
            .write(self.spie as usize)
            .bits(UBE_POS)
            .write(self.ube as usize)
            .bits(MPIE_POS)
            .write(self.mpie as usize)
            .bits(SPP_POS)
            .write(self.spp.into())
            .bits(VS_POS)
            .write(self.vs as usize)
            .bits(MPP_POS)
            .write(self.mpp.into())
            .bits(FS_POS)
            .write(self.fs as usize)
            .bits(XS_POS)
            .write(self.xs as usize)
            .bits(MPRV_POS)
            .write(self.mprv as usize)
            .bits(SUM_POS)
            .write(self.sum as usize)
            .bits(MXR_POS)
            .write(self.mxr as usize)
            .bits(TVM_POS)
            .write(self.tvm as usize)
            .bits(TW_POS)
            .write(self.tw as usize)
            .bits(TSR_POS)
            .write(self.tsr as usize)
            .bits(MBE_POS)
            .write(self.mbe as usize)
            .bits(SD_POS)
            .write(self.sd as usize);

        if cfg!(target_arch = "riscv64") {
            ret.bits(UXL_POS)
                .write(self.uxl as usize)
                .bits(SXL_POS)
                .write(self.sxl as usize)
                .bits(SBE_POS)
                .write(self.sbe as usize)
        } else {
            ret
        }
    }
}
impl Cache {
    pub fn flush(self) {
        let reg_val = self.into();
        write(reg_val)
    }
}

/// 扩展上下文状态
pub enum ExtCtxStatus {
    Off = 0,
    Initial = 1,
    Clean = 2,
    Dirty = 3,
}
impl TryFrom<usize> for ExtCtxStatus {
    type Error = &'static str;

    fn try_from(value: usize) -> Result<Self, Self::Error> {
        if value > 3 {
            Err("Invalid context status range")
        } else {
            Ok(Self::from_raw_unchecked(value))
        }
    }
}
impl ExtCtxStatus {
    fn from_raw_unchecked(value: usize) -> Self {
        match value {
            0 => ExtCtxStatus::Off,
            1 => ExtCtxStatus::Initial,
            2 => ExtCtxStatus::Clean,
            _ => ExtCtxStatus::Dirty,
        }
    }
}
